package data.composition.factory.impl;

import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ReflectUtil;
import data.composition.factory.core.*;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author ZhangJinyu
 * @since 2024-12-13
 */
public class CollectionDataCompositionImpl<T> implements CollectionDataComposition<T> {
    private final Collection<T> dataList;
    private final List<FromWrapperInner<T, ?, ?>> fromWrappers;
    private final Map<String, Field> dataFieldMap;
    private Predicate<? super T> predicate;

    public CollectionDataCompositionImpl(Collection<T> dataList) {
        this.dataList = dataList;
        this.fromWrappers = new LinkedList<>();
        if (Objects.isNull(dataList) || dataList.isEmpty()) {
            dataFieldMap = Collections.emptyMap();
        } else {
            T next = dataList.iterator().next();
            dataFieldMap = Arrays.stream(ReflectUtil.getFields(next.getClass())).collect(Collectors.toMap(Field::getName, v -> v));
        }
    }

    @Override
    public <R, V> FromWrapper<T, R, V> from(R source, Function<R, V> kv) {
        List<R> dataList = new LinkedList<>();
        dataList.add(source);
        return from(dataList, kv);
    }

    @Override
    public <R, V> FromWrapper<T, R, V> from(Collection<R> source, Function<R, V> kv) {
        FromWrapperImpl<T, R, V> fromWrapper = new FromWrapperImpl<>(this, source, kv);
        fromWrappers.add(fromWrapper);
        return fromWrapper;
    }

    @Override
    public void composition() {
        if (Objects.isNull(dataList) || dataList.isEmpty()) {
            return;
        }
        for (T data : dataList) {
            if (predicate.test(data)) {
                for (FromWrapperInner<T, ?, ?> fromWrapper : fromWrappers) {
                    Map<?, ?> fromDataMap = fromWrapper.getFromDataMap();
                    processMapping(data, fromWrapper, fromDataMap);
                    processCollectionMapping(data, fromWrapper, fromDataMap);
                }
            }
        }

    }

    @SuppressWarnings("unchecked")
    private void processMapping(T data, FromWrapperInner<T, ?, ?> fromWrapper, Map<?, ?> fromDataMap) {
        List<? extends MappingInner<T, ?, ?>> mappingInners = fromWrapper.getMappingInners();
        for (MappingInner<T, ?, ?> mappingInner : mappingInners) {
            Object dataValue = mappingInner.getDataFunction().apply(data);
            if (Objects.isNull(dataValue)) {
                continue;
            }
            List<MapObj> mapObjs = mappingInner.getMapObjs();
            if (mapObjs.isEmpty()) {
                continue;
            }
            for (MapObj mapObj : mapObjs) {
                Field field = dataFieldMap.get(LambdaUtil.getFieldName(mapObj.getLeft()));
                field.setAccessible(true);
                Function<Object, Object> right = (Function<Object, Object>) mapObj.getRight();
                try {
                    field.set(data, right.apply(fromDataMap.get(dataValue)));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void processCollectionMapping(T data, FromWrapperInner<T, ?, ?> fromWrapper, Map<?, ?> fromDataMap) {
        List<? extends CollectionMappingInner<T, ?, ?>> collectionMappings = fromWrapper.getCollectionMappings();
        for (CollectionMappingInner<T, ?, ?> collectionMapping : collectionMappings) {
            Collection<?> dataValues = collectionMapping.getDataFunction().apply(data);
            if (Objects.isNull(dataValues)) {
                continue;
            }
            List<MapObj> mapObjs = collectionMapping.getMapObjs();
            if (mapObjs.isEmpty()) {
                continue;
            }
            List<?> sourceList = dataValues.stream().map(fromDataMap::get).filter(Objects::nonNull).toList();
            for (MapObj mapObj : mapObjs) {
                Function<Object, Object> right = (Function<Object, Object>) mapObj.getRight();
                List<Object> list = sourceList.stream().map(right).collect(Collectors.toList());
                Field field = dataFieldMap.get(LambdaUtil.getFieldName(mapObj.getLeft()));
                field.setAccessible(true);
                try {
                    field.set(data, list);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @Override
    public CollectionDataComposition<T> filter(Predicate<? super T> predicate) {
        this.predicate = predicate;
        return this;
    }
}
